<template>
  <div class="webrtc-demo">
    <el-row :gutter="20">
      <el-col :span="24">
        <el-form :inline="true" :model="formInline" class="demo-form-inline">
          <el-form-item label="选择摄像头">
            <el-select v-model="formInline.videoId" placeholder="摄像头">
              <el-option
                v-for="(item, index) in localDevice.videoIn"
                :key="index"
                :label="item.label"
                :value="item.id"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="选择麦克风">
            <el-select v-model="formInline.audioInId" placeholder="麦克风">
              <el-option
                v-for="(item, index) in localDevice.audioIn"
                :key="index"
                :label="item.label"
                :value="item.id"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="选择听筒">
            <el-select v-model="formInline.audioOutId" placeholder="听筒">
              <el-option
                v-for="(item, index) in localDevice.audioOut"
                :key="index"
                :label="item.label"
                :value="item.id"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="分辨率:Width">
            <el-input v-model="formInline.width" />
          </el-form-item>
          <el-form-item label="分辨率:Height">
            <el-input v-model="formInline.height" />
          </el-form-item>
          <el-form-item label="FPS">
            <el-input v-model="formInline.frameRate" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit">确定</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
    <el-row>
      <video id="localdemo01" autoplay controls muted></video>
    </el-row>
  </div>
</template>

<script setup>
const localDevice = reactive({
  audioIn: [],
  videoIn: [],
  audioOut: [],
});

const formInline = reactive({
  videoId: undefined,
  audioInId: undefined,
  audioOutId: undefined,
  width: 1280,
  height: 720,
  frameRate: 15,
});
const handleError = (error) => {
  console.log("摄像头无法正常使用，请检查是否缺失");
  console.error(
    "navigator.MediaDevices.getUserMedia error: ",
    error.message,
    error.name
  );
};
/**
 * 初始化本地设备
 * @returns {void}
 */
const initInnerLocalDevice = () => {
  // 定义一个constraints变量，用于设置音频和视频是否可用
  let constraints = {
    audio: true,
    video: true,
  };

  // 判断浏览器是否支持获取媒体设备
  if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
    console.log("浏览器不支持获取媒体设备");
    return;
  }
  // 使用then函数获取用户媒体流
  navigator.mediaDevices
    .getUserMedia(constraints)
    .then((stream) => {
      // 停止流
      stream.getTracks().forEach((trick) => {
        trick.stop();
      });

      // 遍历可用的媒体设备
      navigator.mediaDevices
        .enumerateDevices()
        .then((devices) => {
          devices.forEach((device) => {
            // 定义一个obj变量，用于存储设备信息
            let obj = {
              id: device.deviceId,
              kind: device.kind,
              label: device.label,
            };
            // 判断设备类型是否为音频输入
            if (device.kind === "audioinput") {
              // 判断本地设备中音频输入是否已存在该设备
              if (
                localDevice.audioIn.filter((e) => e.id === device.deviceId)
                  .length === 0
              ) {
                // 将设备添加到本地设备音频输入中
                localDevice.audioIn.push(obj);
              }
            }
            // 判断设备类型是否为音频输出
            if (device.kind === "audiooutput") {
              // 判断本地设备中音频输出是否已存在该设备
              if (
                localDevice.audioOut.filter((e) => e.id === device.deviceId)
                  .length === 0
              ) {
                // 将设备添加到本地设备音频输出中
                localDevice.audioOut.push(obj);
              }
            } else if (device.kind === "videoinput") {
              // 判断设备类型是否为视频输入
              if (
                localDevice.videoIn.filter((e) => e.id === device.deviceId)
                  .length === 0
              ) {
                // 将设备添加到本地设备视频输入中
                localDevice.videoIn.push(obj);
              }
            }
          });
        })
        .catch(handleError);
    })
    .catch(handleError);
};
const onSubmit = async () => {
  // 获取DOM元素
  let domId = "localdemo01";
  let video = document.getElementById(domId);
  let stream = video.srcObject;
  // 移除音频和视频流
  if (stream) {
    stream.getAudioTracks().forEach((e) => {
      stream.removeTrack(e);
    });
    stream.getVideoTracks().forEach((e) => {
      stream.removeTrack(e);
    });
  }
  // 获取目标设备媒体
  let newStream = await getTargetDeviceMedia(
    formInline.videoId,
    formInline.audioInId
  );
  // 将新的流设置为视频的源
  video.srcObject = newStream;
  video.muted = true;
};
/**
 * 获取本地用户媒体
 * @param {Object} constraints 约束条件
 * @returns {Promise<MediaStream>} 返回一个Promise对象，用于获取本地用户媒体
 */
const getLocalUserMedia = async (constraints) => {
  // 获取本地用户媒体
  return await navigator.mediaDevices.getUserMedia(constraints);
};

/**
 * 获取目标设备媒体
 * @param {String} videoId 视频设备ID
 * @param {String} audioId 音频设备ID
 * @returns {Promise<MediaStream>} 返回一个Promise对象，用于获取目标设备媒体
 * @throws {Error} 如果获取目标设备媒体失败，则抛出错误
 */
const getTargetDeviceMedia = async (videoId, audioId) => {
  // 设置约束条件
  const constraints = {
    audio: { deviceId: audioId ? { exact: audioId } : undefined },
    video: {
      deviceId: videoId ? { exact: videoId } : undefined,
      width: formInline.width,
      height: formInline.height,
      frameRate: { ideal: formInline.frameRate, max: 24 },
    },
  };

  // 如果存在窗口的流，则停止它
  if (window.stream) {
    window.stream.getTracks().forEach((track) => {
      track.stop();
    });
  }
  // 返回目标设备媒体
  return await getLocalUserMedia(constraints).catch(handleError);
};

onMounted(() => {
  initInnerLocalDevice();
});
</script>

<style lang="scss" scoped>
.webrtc-demo {
  padding: 20px;
  position: relative;
}
#localdemo01 {
  width: 500px;
  height: 400px;
}
</style>
